Skip to content

feat: add DocumentProgressCard component#338

Merged
interacsean merged 10 commits into
mainfrom
feat/core/784-document-progress-card
Jul 1, 2026
Merged

feat: add DocumentProgressCard component#338
interacsean merged 10 commits into
mainfrom
feat/core/784-document-progress-card

Conversation

@interacsean

@interacsean interacsean commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds DocumentProgressCard — a presentational card for a transactional document's fulfilment / lifecycle state: a derived completion percentage, a stacked progress bar, and a received / returned / yet-to-receive legend. Ported from the omakase FulfilmentRate component as a view-only, props-driven component (no data-fetching or domain logic).

Implements tailor-inc/platform-planning#784 (Document Progress Card), AppShell v1.6.0. Matches the Figma design.

Screenshot

image

API

<DocumentProgressCard
  received={{ value: 12 }}
  returned={{ value: 2 }}
  yetToReceive={{ value: 28 }}
/>
  • Three opinionated buckets — received / returned / yetToReceive — each { value, label?, color? } with per-bucket label/color defaults (indigo / pink / neutral).
  • Percentage is derived, not passed: received / (received + yetToReceive). returned is a subset of received, so it doesn't change the denominator. Pass returnedCountsAsComplete={false} to subtract returns from progress.
  • title defaults to "Fulfilment rate". Curated color palette (indigo/pink/green/amber/red/blue/neutral).
  • Bar fills with a net-received segment + a returned segment over a muted track; the unfilled remainder represents yet-to-receive.

Changes

  • packages/core/src/components/document-progress-card/ — component (Pattern C), types.ts, index.ts, tests
  • Public export from packages/core/src/index.ts (minimal surface: component + Props type)
  • docs/components/document-progress-card.md, catalogue entry, changeset (minor)
  • examples/vite-app/dashboard/document-progress showcase page + sidebar link

Testing

  • 17 tests (12 behavioral + 5 snapshot); full suite: type-check 8/8, lint 0 errors, 1184 tests, fmt clean
  • Verified visually in the vite example (showcase page + record-detail right rail)

🤖 Generated with Claude Code

interacsean and others added 4 commits June 27, 2026 11:00
A presentational card for a transactional document's fulfilment state:
a derived completion percentage, a stacked progress bar, and a
received / returned / yet-to-receive legend. Ported from the omakase
FulfilmentRate component as a view-only, props-driven component — no
data-fetching or domain logic.

Each bucket takes { value, label?, color? } with per-bucket defaults.
Percent is derived as received / (received + yetToReceive); pass
returnedCountsAsComplete={false} to subtract returns from progress.

Includes behavioral + snapshot tests, docs, catalogue entry, and a changeset.

Refs tailor-inc/platform-planning#784

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a /dashboard/document-progress example page demonstrating the
component's states (empty, partial, returns-subtracted, complete,
relabelled/custom-colors, returns-heavy) and a sidebar nav link.

Refs tailor-inc/platform-planning#784

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adversarial review surfaced two ways the bar could contradict the
header percentage:

- With returnedCountsAsComplete={false}, the header showed
  (received − returned)/total but the bar still filled received/total.
  The returned segment is now part of the colored fill only when it
  counts as complete, so the fill always equals the header percentage.
- When returned > received, the net-received clamp left a returned
  segment wider than the header implied. returned is now clamped to
  received before deriving the breakdown.

Also sanitize incoming amounts (non-finite/negative → 0) so invalid
input can't render NaN/negative figures in the legend or break the bar.

Adds tests for both consistency cases and sanitization; updates docs
and the affected snapshot.

Refs tailor-inc/platform-planning#784

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Treat the bar as a composition rather than a pure progress fill: it
always renders a net-received segment (received − returned) followed by
a returned segment, in both modes. The header percentage maps onto the
net-received segment, plus the returned segment when
returnedCountsAsComplete is true.

This keeps returned items visible in the bar even when they're excluded
from the rate (returnedCountsAsComplete={false}), matching the Figma /
omakase intent, while the net-received segment still equals the header.

Retains the returned≤received clamp and input sanitization. Updates the
affected test, snapshot, and docs.

Refs tailor-inc/platform-planning#784

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@interacsean interacsean marked this pull request as ready for review June 27, 2026 10:58
@interacsean interacsean requested a review from a team as a code owner June 27, 2026 10:58
@IzumiSy

IzumiSy commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

/review

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

API Design Review completed successfully!

API Design Review complete for PR #338 (DocumentProgressCard). Found 2 Low-severity issues (missing public exports for DocumentProgressItem/DocumentProgressColor; incomplete JSDoc on returnedCountsAsComplete). No High or Medium issues. Verdict: Approve.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by API Design Review for issue #338 · 366.7 AIC · ⌖ 12.5 AIC · ⊞ 26.3K
Comment /review to run again

Comment thread packages/core/src/index.ts
Comment thread packages/core/src/components/document-progress-card/types.ts Outdated
…t-progress-card

# Conflicts:
#	examples/vite-app/src/App.tsx
@IzumiSy

IzumiSy commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@interacsean

I’m curious how you’re thinking about the long-term scope of this API. Right now the public shape is fixed to three buckets — received, returned, and yetToReceive — even though the labels are customizable.

Is the intent for this component to stay specific to that receiving/returning model, or do you expect it to be reused for other document workflows as well? In ERP-style flows, it feels fairly common to need an additional status such as cancelled, blocked, or backordered, so I wanted to check how far we expect this API to stretch.


If broader stretch is not really a goal here, I wonder whether we should at least make the three fixed buckets more generic at the API level as well, instead of baking the receiving-specific terminology into the public props. For example:

type DocumentProgressValue = {
  value: number;
  label: string;
  color?: DocumentProgressColor;
};

type DocumentProgressCardProps = {
  title?: string;
  values: {
    complete: DocumentProgressValue;
    subtract: DocumentProgressValue;
    remaining: DocumentProgressValue;
  };
  className?: string;
};

That would still preserve the current three-slot model and progress calculation, but make the component easier to reuse across adjacent workflows.

Happy to hear your thought if we are on this direction.

interacsean and others added 2 commits June 30, 2026 13:42
… wrapper

Per PR review: make DocumentProgressCard generic so it stretches to any
document workflow (shipped/cancelled/backordered/…), and move the
receiving-specific logic into a thin opinionated wrapper.

- DocumentProgressCard: now takes arbitrary `segments` + an explicit
  `percent`, with an optional `legend` override and `total` (for an
  unfilled track remainder). No baked-in domain terminology.
- ProcurementFulfilmentProgressCard: new wrapper keeping the existing
  received/returned/yetToReceive API and business logic (derived
  percentage, returned≤received clamp, net-received/returned bar
  decomposition, sanitization) — composed over the generic card. The
  legend still shows the full received amount while the bar decomposes.

Both are exported. Updates tests (generic + wrapper), snapshots, docs
(two pages), catalogue entries, the changeset, and the showcase page.

Addresses #338 (comment)
Refs tailor-inc/platform-planning#784

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per review follow-up:

- DocumentProgressCard: `color` is now required on each segment — drop
  the by-position default palette so colors are always explicit.
- ProcurementFulfilmentProgressCard: correct the domain wording from
  "goods receipt" to "purchase order" across the component doc comment,
  docs, catalogue, changeset, and example (it tracks PO fulfilment).

Updates tests and docs accordingly.

Refs tailor-inc/platform-planning#784

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m strongly against including ProcurementFulfilmentProgressCard in AppShell.

This component is not just “opinionated”; it is explicitly domain-specific. Its API encodes procurement concepts directly (received, returned, yetToReceive), so adding it to AppShell would mean expanding the core library into business-domain UI. Once we allow that, it becomes much easier for more domain-specific components to accumulate in AppShell over time.

I think the right boundary is:

  • AppShell should provide the generic primitive, such as DocumentProgressCard
  • Domain-specific components should live outside AppShell

In particular, I think this kind of component belongs in erp-kit, and more specifically should be part of a module frontend template there, rather than part of AppShell core. That keeps AppShell focused on reusable platform-level building blocks, while erp-kit owns the ERP/domain layer.

If we have a real cross-domain need, we should extract the generic abstraction into AppShell — not promote a procurement-specific wrapper into core.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hear what you're saying and I agree but have strange feeling. I am a little unclear on the overall strategy on domain-opinionated components. We want some consistency for something like Purchase Order (which you are saying should live in erp-kit... I understand why). If components in erp-kit is an established strategy, perhaps we need an announcement and education on it. Though something feels odd to me and counterproductive having component definitions in different places (erp-kit vs app-shell), and sometimes having a library of components and sometimes having templates, it's all a bit confusing and lacking guidance. I will work on some draft definitions to help me understand and we can reason about it as a group.

I will remove the ProcurementFulfilmentProgressCard from here, to align with AppShell being domain-agnostic, but in the patterns I will create a Purchase Order example for the DocumentProgressCard implementation. At least then there's something not strict that can be used as a guide in basically what is the place this component will be used most

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with your point here. The boundary between AppShell, erp-kit, and templates still feels underdefined.

If we want to keep building domain-specific components like ProcurementFulfilmentProgressCard, I think we also need to move the erp-kit frontend-template initiative forward in parallel. Otherwise, we end up agreeing that they should not live in AppShell, but still do not have a clear first-party place for them to go.

So this feels like a good signal that we should invest in the erp-kit side soon.

interacsean and others added 2 commits July 1, 2026 13:41
Team feedback: the wrapper was too domain-specific. Remove it and keep
only the generic DocumentProgressCard.

The purchase-order fulfilment use case now lives as guidance rather than
a component: the docs gain a complete, copyable "purchase-order
fulfilment" recipe (labels, indigo/pink/neutral colors, net-received +
returned bar split, legend override, derived percentage with the
returned-counts-as-complete toggle), and the example app demonstrates
the same pattern via a small local PurchaseOrderFulfilment helper.

Removes the component, its tests, snapshot, docs page, catalogue entry,
and public export. Updates the changeset.

Refs tailor-inc/platform-planning#784

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@interacsean interacsean merged commit 820b75b into main Jul 1, 2026
5 checks passed
@interacsean interacsean deleted the feat/core/784-document-progress-card branch July 1, 2026 23:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants